home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d18 / gsdb21.arc / GS_DBASE.PAS < prev    next >
Pascal/Delphi Source File  |  1991-01-04  |  49KB  |  1,023 lines

  1. {                           dBase III File Handler
  2.  
  3.        GS_DBASE Copyright (c)  Richard F. Griffin
  4.  
  5.        15 November 1990
  6.  
  7.        102 Molded Stone Pl
  8.        Warner Robins, GA  31088
  9.  
  10.        -------------------------------------------------------------
  11.        This unit handles the objects for all dBase III file (.DBF)
  12.        operations.
  13.  
  14.                    SHAREWARE  -- COMMERCIAL USE RESTRICTED
  15.  
  16.  
  17.  
  18.        Changes:
  19.  
  20.        16 Nov 90 - Moved Pack method to GS_dBFld.
  21.  
  22.  
  23. }
  24. {
  25.                            ┌──────────────────────┐
  26.                            │  INTERFACE SECTION:  │
  27.                            └──────────────────────┘
  28. }
  29.  
  30. unit GS_DBASE;
  31.  
  32. interface
  33.  
  34. uses
  35.      CRT,
  36.      DOS,
  37.      GS_KeyI,
  38.      GS_FileH,    {File handler}
  39.      GS_Strng,    {String handling Routines}
  40.      GS_Error,    {Error Handling routines}
  41.      GS_DBNdx;    {Unit for index operations (.NDX files)}
  42.  
  43. const
  44.    GS_dBase_MaxRecBytes = 4000;      {dBASE III record limit }
  45.    GS_dBase_MaxRecField = 128;       {dBASE III field limit}
  46.    GS_dBase_MaxMemoRec  = 512;       {Size of each block of memo file data}
  47.  
  48.    Next_Record = -1;   {Token value passed to read next record}
  49.    Prev_Record = -2;   {Token value passed to read previous record}
  50.    Top_Record  = -3;   {Token value passed to read first record}
  51.    Bttm_Record = -4;   {Token value passed to read final record}
  52.  
  53.    GS_dBase_UnDltChr = 32;   {Character for Undeleted Record}
  54.    GS_dBase_DltChr   = 42;   {Character for Deleted Record}
  55.  
  56. type
  57.  
  58.    GS_dBase_Status = (NotOpen, NotUpdated, Updated);
  59.            {Flags to indicate status of dBase III file }
  60.  
  61.    GS_dBase_dRec = ^GS_dBase_DataRecord;
  62.            {Pointer type used in object descriptions to locate the memory}
  63.            {array in bytes for the dBase record.  Uses GS_dBase_DataRecord}
  64.            {defined below.}
  65.  
  66.    GS_dBase_DataRecord = ARRAY[0..GS_dBase_MaxRecBytes] OF Byte;
  67.            {Defines an array of bytes in memory that is as large as the }
  68.            {maximum size of a dBase record (GS_dBase_MaxRecBytes).}
  69.  
  70. {
  71.         ┌──────────────────────────────────────────────────────────────────┐
  72.         │  ********         Data Structure Description         **********  │
  73.         │                                                                  │
  74.         │  The following record defines the dBase III file header.  Refer  │
  75.         │  to Appendix A for an explanation of each data element.          │
  76.         └──────────────────────────────────────────────────────────────────┘
  77. }
  78.    GS_dBase_Head = Record
  79.                       DBType     : Byte;
  80.                       Year       : Byte;
  81.                       Month      : Byte;
  82.                       Day        : Byte;
  83.                       RecCount   : LongInt;
  84.                       Location   : Integer;
  85.                       RecordLen  : Integer;
  86.                       Reserved   : Array[1..20] of Byte;
  87.                    end;
  88.  
  89. {
  90.      ┌──────────────────────────────────────────────────────────────────┐
  91.      │  *********             Field Descriptor              *********   │
  92.      │                                                                  │
  93.      │  This record defines the field descriptor.  There is one of      │
  94.      │  these for each field defined in the database structure.  They   │
  95.      │  are stacked as 32 bytes following the file header record, as    │
  96.      │  described in Appendix A.                                        │
  97.      └──────────────────────────────────────────────────────────────────┘
  98. }
  99.  
  100.    GS_dBase_Field = Record
  101.                        FieldName    : String[10];
  102.                                       {Array[1..11] of Char actually}
  103.                                       {This is to simplify conversion}
  104.                        FieldType    : Char;
  105.                        FieldAddress : LongInt;
  106.                        FieldLen     : Byte;
  107.                        FieldDec     : Byte;
  108.                        Reserved     : Array[1..14] of Char;
  109.                     end;
  110.  
  111.    GS_dBase_dFld = ^GS_dBase_DataField;
  112.           {Pointer type used in object descriptions to assign memory}
  113.           {for storing the field descriptors.                          }
  114.  
  115.    GS_dBase_DataField = ARRAY[1..GS_dBase_MaxRecField] OF GS_dBase_Field;
  116.           {Defines an array of field descriptors (GS_dBase_Field) that}
  117.           {is as large as the maximum number of dBase fields allowed}
  118.           {(GS_dBase_MaxRecFields).}
  119.  
  120.    GS_dBase_nFld = ^GS_dBase_NameField;
  121.           {Pointer type used in object descriptions to assign memory}
  122.           {for storing the field name strings.                      }
  123.  
  124.    GS_dBase_NameField = Array[1..GS_dBase_MaxRecField] OF string[11];
  125.           {Defines an array of field name strings (GS_dBase_Field) that}
  126.           {is as large as the maximum number of dBase fields allowed}
  127.           {(GS_dBase_MaxRecFields).}
  128.  
  129.  
  130. {
  131.        ┌──────────────────────────────────────────────────────────────┐
  132.        │  ***********      dBase Object Definition      ************  │
  133.        └──────────────────────────────────────────────────────────────┘
  134. }
  135.  
  136.    GS_dBase_DB = object(GS_KeyI_Objt) {Make it a child for keyboard control}
  137.       FileName     : string[64];      {Stores FileName of dBase File}
  138.       dFile        : file;            {File Type to reference data file}
  139.       mFile        : file;            {File Type to reference memo file}
  140.       HeadProlog   : GS_dBase_Head;   {Image of file header}
  141.       dStatus      : GS_dBase_Status; {Holds Status Code of file}
  142.       WithMemo     : Boolean;         {True if memo file present}
  143.       DateOfUpdate : string[8];       {MM/DD/YY of last update}
  144.       NumRecs      : LongInt;         {Number of records in file}
  145.       HeadLen      : Integer;         {Header + Field Descriptor length}
  146.       RecLen       : Integer;         {Length of record}
  147.       NumFields    : Integer;         {Number of fields in the record}
  148.       Fields       : GS_dBase_dFld;   {Pointer to memory array holding}
  149.                                       {field descriptors}
  150.       FieldsN      : GS_dBase_nFld;   {Pointer to memory array holding}
  151.                                       {Field name strings}
  152.       RecNumber    : LongInt;         {Physical record number last read}
  153.       CurRecord    : GS_dBase_dRec;   {Pointer to memory array holding}
  154.                                       {the current record data.  Refer}
  155.                                       {to Appendix B for record structure}
  156.       DelFlag      : boolean;         {True if record deleted}
  157.       File_EOF     : boolean;         {True if at end of file }
  158.       Found        : boolean;         {Set True on valid record Find}
  159.       dbfNdxTbl    : array [1..16] of GS_Indx_LPtr;
  160.                                       {Holds addresses of up to 16 Index}
  161.                                       {Objects.  The first array is the}
  162.                                       {Master Index.  For File changes,}
  163.                                       {this array will be used to ensure}
  164.                                       {all indexes are updated. }
  165.       dbfNdxActv   : boolean;         {True if an index file is used}
  166.  
  167. {
  168.    ┌───────────────────────────────────────────────────────────────────────┐
  169.    │  ***  These methods are described individually in the following  ***  │
  170.    │        pages.  As seen here, their name describes their function      │
  171.    └───────────────────────────────────────────────────────────────────────┘
  172. }
  173.  
  174.       PROCEDURE Append;
  175.       PROCEDURE Blank;
  176.       PROCEDURE Close;
  177.       FUNCTION  Create(FName : string) : boolean;
  178.       PROCEDURE Delete;
  179.       FUNCTION  Find(st : string) : boolean;
  180.       FUNCTION  Formula(st : string) : string; virtual;
  181.       PROCEDURE GetRec(RecNum: LongInt);
  182.       PROCEDURE Index(IName : String);
  183.       PROCEDURE Index_List(RecAct: LongInt; var I_List; var RNum : longint);
  184.       CONSTRUCTOR Init(FName : string);
  185.       PROCEDURE Open;
  186.       PROCEDURE PutRec(RecNum : LongInt);
  187.       PROCEDURE UnDelete;
  188.    end;
  189.  
  190. {
  191.                          ┌──────────────────────────┐
  192.                          │  IMPLEMENTATION SECTION  │
  193.                          └──────────────────────────┘
  194. }
  195.  
  196. implementation
  197. uses
  198.    GS_dB3Wk;                          {Use shown here to avoid circular def.}
  199.  
  200.  
  201. CONST
  202.   DB3File = 3;                        {First byte of dBase III(+) file}
  203.   DB3WithMemo = $83;                  {First byte of dBase III(+) file}
  204.                                       {if memo file (.DBT) is present }
  205.  
  206.  
  207. PROCEDURE GS_dBase_DB.Append;
  208. BEGIN
  209.    PutRec(0);
  210.         {Calls objectname.PutRec method with a record number of}
  211.         {zero.  This causes the record number to default to }
  212.         {objectname.NumRecs + 1.                              }
  213. END;
  214.  
  215.  
  216. PROCEDURE GS_dBase_DB.Blank;
  217. begin
  218.    FillChar(CurRecord^[0], RecLen, ' ');
  219.                                       {Fill spaces for RecLen bytes}
  220. end;
  221.  
  222.  
  223. PROCEDURE GS_dBase_DB.Close;
  224. CONST
  225.    EofMark : Byte = $1A;              {ASCII code for EOF byte}
  226. var
  227.    rsl,
  228.    yy, mm, dd, wd : word;             {Local variables to get today's}
  229.                                       {date through TP's GetDate procedure}
  230.    i              : integer;          {work variable}
  231. {
  232.        ┌──────────────────────────────────────────────────────────────┐
  233.        │   The Update_File procedure is called if any records are     │
  234.        │   added/updated while the file is open.  This is indicated   │
  235.        │   by objectname.dStatus set to 'UpDated'.  The procedure     │
  236.        │   inserts the current date in the file header, updates the   │
  237.        │   record count, rewrites the file header, and writes an EOF  │
  238.        │   byte at the end of the file.                               │
  239.        └──────────────────────────────────────────────────────────────┘
  240. }
  241.    procedure UpDate_File;
  242.    BEGIN
  243.       GetDate (yy,mm,dd,wd);          {Call TP's GetDate procedure}
  244.       HeadProlog.year := yy-1900;     {Extract the Year}
  245.       HeadProlog.month := mm;         {Extract the Month}
  246.       HeadProlog.day := dd;           {Extract the Day}
  247.       HeadProlog.RecCount := NumRecs; {Update number records in file}
  248.       GS_FileWrite(dFile, 0, HeadProlog, 8, rsl);
  249.       GS_FileWrite(dFile, HeadLen+NumRecs*RecLen, EofMark, 1, rsl); {EOF marker}
  250.    END;   { IF Updated }
  251.  
  252. {
  253.          ┌───────────────────────────────────────────────────────────┐
  254.          │  Beginning of CLOSE Procedure.                            │
  255.          │      1.  Exit if file not open                            │
  256.          │      2.  Update the file header if records added/updated  │
  257.          │      3.  Close the file                                   │
  258.          │      4.  Close the .DBT memo file if applicable           │
  259.          │      5.  Set objectname.dStatus to 'NotOpen'              │
  260.          └───────────────────────────────────────────────────────────┘
  261. }
  262.  
  263. begin
  264.    IF dStatus = NotOpen THEN exit;    {Exit if file not open}
  265.    IF dStatus = Updated THEN UpDate_File;
  266.                                       {Write new header information if the}
  267.                                       {file was updated in any way}
  268.    GS_FileClose(dFile);
  269.    if WithMemo then GS_FileClose(mFile);
  270. {
  271.          ┌──────────────────────────────────────────────────────────┐
  272.          │  The following routine releases index files associated   │
  273.          │  with the .DBF file and releases memory.                 │
  274.          └──────────────────────────────────────────────────────────┘
  275. }
  276.    i := 1;                         {initialize counter}
  277.    while dbfNdxTbl[i] <> nil do
  278.    begin
  279.       dbfNdxTbl[i]^.Ndx_Close;     {Close this index file}
  280.       dispose(dbfNdxTbl[i]);       {Release Heap Memory}
  281.       dbfNdxTbl[i] := nil;         {set pointer to 'empty'}
  282.       inc(i);                      {increment counter}
  283.    end;
  284.    dbfNdxActv := false;
  285.    dStatus := NotOpen;             {Set objectname.dStatus to 'NotOpen'}
  286. END;                        { GS_dBase_Close }
  287.  
  288.  
  289. Function GS_dBase_DB.Create(FName : string) : boolean;
  290. begin
  291.    if GS_dB3_Create(FName) then Create := true else Create := false;
  292. END;                        { GS_dBase_Create }
  293.  
  294.  
  295. PROCEDURE GS_dBase_DB.Delete;
  296. begin
  297.    DelFlag := true;                   {Set Delete Flag to true}
  298.    CurRecord^[0] := GS_dBase_DltChr;  {Put '*' in first byte of current record}
  299.    PutRec(RecNumber);                 {Write the current record to disk }
  300. end;                 {GS_dBase_Delete}
  301.  
  302.  
  303. {
  304.                                    FIND
  305.  
  306.  
  307.      ╔══════════════════════════════════════════════════════════════════╗
  308.      ║                                                                  ║
  309.      ║   The FIND method will search the master index file for the      ║
  310.      ║   key string contained in the calling argument.                  ║
  311.      ║                                                                  ║
  312.      ║   Note:  At this time, numeric fields must have a string value   ║
  313.      ║          argument, and date fields are not handled.              ║
  314.      ║                                                                  ║
  315.      ║       Calling the Method:                                        ║
  316.      ║                                                                  ║
  317.      ║           objectname.Find(String)                                ║
  318.      ║                                                                  ║
  319.      ║               ( where objectname is of type GS_dBase_DB,         ║
  320.      ║                       String is key value to match)              ║
  321.      ║                                                                  ║
  322.      ║       Result:                                                    ║
  323.      ║                                                                  ║
  324.      ║           Matching record is read if found.  No error check,     ║
  325.      ║           but index object Found flag is set true on match.      ║
  326.      ║                                                                  ║
  327.      ╚══════════════════════════════════════════════════════════════════╝
  328. }
  329.  
  330. Function GS_dBase_DB.Find(st : string) : boolean;
  331. var
  332.    RNum   : longint;
  333. begin
  334. {
  335.          ┌───────────────────────────────────────────────────────────┐
  336.          │  The next statement checks to see if an index is active   │
  337.          │  (dbfNdxActv = true), and calls the index object's        │
  338.          │  KeyFind method if true.  The key string is passed to     │
  339.          │  the method as the only argument.  The matching record    │
  340.          │  is returned from the method.  If there is no match,      │
  341.          │  the method returns a zero value.  Note that the method   │
  342.          │  is called using the first index object pointer in array  │
  343.          │  dbfNdxTabl (the master index).  The ability to use an    │
  344.          │  object pointer in place of an actual object is a highly  │
  345.          │  useful tool.                                             │
  346.          └───────────────────────────────────────────────────────────┘
  347. }
  348.    if (dbfNdxActv) then
  349.    begin
  350.       RNum := dbfNdxTbl[1]^.KeyFind(st);
  351.       if RNum > 0 then                {RNum = 0 if no match, otherwise}
  352.                                       {it holds the valid record number}
  353.       begin
  354.          GetRec(RNum);                {If match found, read the record}
  355.          Found := True;               {Set Match Found flag true}
  356.       end else
  357.       begin                           {If no matching index key, then}
  358.          Found := False;              {Set Match Found Flag False}
  359.       end;
  360.    end else                           {If there is no index file, then}
  361.       Found := False;                 {Set Match Found Flag False}
  362.    Find := Found;
  363. end;                  {GS_dBase_Find}
  364.  
  365.  
  366. function GS_dBase_DB.Formula(st : string) : string;
  367. begin
  368.    ShowError(399,'Object for field handling missing');
  369.    Formula := '';
  370. end;
  371.  
  372.  
  373. {
  374.                                    GETREC
  375.  
  376.  
  377.    ╔═══════════════════════════════════════════════════════════════════════╗
  378.    ║                                                                       ║
  379.    ║  The GETREC method will access the dBase III file to retrieve the     ║
  380.    ║  record number passed in the call.                                    ║
  381.    ║                                                                       ║
  382.    ║      Calling the Method:                                              ║
  383.    ║                                                                       ║
  384.    ║            objectname.GetRec (RecNum)                                 ║
  385.    ║                                                                       ║
  386.    ║                   ( where objectname is of type GS_dBase_DB,          ║
  387.    ║                           RecNum is the record number to retrieve.    ║
  388.    ║                           **  If a number greater than 0, record      ║
  389.    ║                               will be physical number from .DBF;      ║
  390.    ║                               if Next_Record, Prev_Record,            ║
  391.    ║                               Top_Record, or Bttm_Record, then        ║
  392.    ║                               the appropriate record will be found.   ║
  393.    ║                               For these codes, if an index is in      ║
  394.    ║                               use, the record will be retrieved       ║
  395.    ║                               based on it's location in the index.)   ║
  396.    ║                                                                       ║
  397.    ║       Result:                                                         ║
  398.    ║                                                                       ║
  399.    ║            1.  Record is retrieved based on record number argument    ║
  400.    ║            2.  Objectname.RecNumber set to record number read         ║
  401.    ║            3.  Objectname.DelFlag set true if deleted record          ║
  402.    ║            4.  If last record of file (.DBF or .NDX), then            ║
  403.    ║                objectname.File_EOF set true.                          ║
  404.    ║                                                                       ║
  405.    ╚═══════════════════════════════════════════════════════════════════════╝
  406. }
  407.  
  408.  
  409. PROCEDURE GS_dBase_DB.GetRec(RecNum : LongInt);
  410. VAR
  411.    dFilea : FileRec absolute dFile;
  412.    i,
  413.    Result : Integer;                  {Local working variable}
  414.    RNum   : LongInt;                  {Local working variable  }
  415.    StrFil : String[80];
  416.    rsl    : word;
  417. BEGIN
  418.    if NumRecs = 0 then
  419.    begin
  420.       File_EOF := true;
  421.       exit;
  422.    end;
  423.    RNum := RecNum;                    {Store RecNum locally for modification}
  424.    File_EOF := false;                 {Initialize End of File Flag to false}
  425.  
  426. {
  427.          ┌───────────────────────────────────────────────────────────┐
  428.          │  The next statement checks to see if an index is active   │
  429.          │  (dbfNdxActv = true), and calls the index object's        │
  430.          │  KeyRead method if true and the record requested is       │
  431.          │  a relative record (less than 0).  Note that the method   │
  432.          │  is called using the first index object pointer in array  │
  433.          │  dbfNdxTabl (the master index).  The ability to use an    │
  434.          │  object pointer in place of an actual object is a highly  │
  435.          │  useful tool.  Upon return, the index file's EOF flag is  │
  436.          │  stored as the .DBF's End-of-File Flag.                   │
  437.          └───────────────────────────────────────────────────────────┘
  438. }
  439.    if (dbfNdxActv) and (RecNum < 0) then
  440.    begin
  441.       RNum := dbfNdxTbl[1]^.KeyRead(RecNum);
  442.                                       {Get record number of physical}
  443.                                       {record to read from .DBF.}
  444.       File_EOF :=dbfNdxTbl[1]^.KeyEOF;
  445.                                       {Get index EOF flag.  The EOF will be}
  446.                                       {set when a KeyRead of Next_Record}
  447.                                       {will go past the last index record}
  448.    end
  449.    else
  450.       if (dbfNdxActv) and (RNum > 0) and  (RNum <= NumRecs) then
  451.          if not dbfNdxTbl[1]^.KeyLocRec(RecNum) then exit;
  452.                                       {If physical record search, set index}
  453.                                       {to the same record.}
  454.    if File_EOF then exit;             {Return if EOF reached}
  455. {
  456.          ┌──────────────────────────────────────────────────────────┐
  457.          │  The value in RNum is tested to see if it is a relative  │
  458.          │  record seek or a physical record number.  The number    │
  459.          │  is also tested to ensure it is in the file record       │
  460.          │  range of valid numbers.  Note, if an index was read,    │
  461.          │  RNum will now be a physical record.                     │
  462.          └──────────────────────────────────────────────────────────┘
  463. }
  464.    case RNum of
  465.       Next_Record : begin
  466.                        RNum := RecNumber + 1;
  467.                                       {Get next sequential record}
  468.                        if RNum > NumRecs then
  469.                        begin          {If beyond number of records in file,}
  470.                                       {you must recover}
  471.                           RNum := NumRecs;
  472.                                       {Reset to final record}
  473.                           File_EOF := true;
  474.                                       {Set EOF Flag to True}
  475.                           exit;       {Return from GetRec}
  476.                        end;
  477.                     end;
  478.       Prev_Record : begin
  479.                        RNum := RecNumber - 1;
  480.                                       {Get Previous Record}
  481.                        if RNum < 1 then RNum := 1;
  482.                                       {If at beginning of file, stay}
  483.                     end;
  484.       Top_Record  : RNum := 1;        {Set to the first record}
  485.       Bttm_Record : RNum := NumRecs;  {Set to the last record}
  486.    end;
  487.    if (RNum < 1) or (RNum > NumRecs) then
  488.    begin                              {if a physical record number is out}
  489.                                       {of range, exit with error}
  490.       i := 0;
  491.       Str(RNum, StrFil);
  492.       StrFil := 'Record ' + StrFil;
  493.       StrFil := StrFil + ' Out of Range for File ';
  494.       while dFilea.Name[i] <> #0 do
  495.       begin
  496.          StrFil := StrFil + dFilea.Name[i];
  497.          inc(i);
  498.       end;
  499.       ShowError(100,StrFil);
  500.       exit;                           {Terminate read attempt if record number}
  501.                                       {is out of range}
  502.    end;
  503.    GS_FileRead(dFile, HeadLen+(RNum-1) * RecLen, CurRecord^, RecLen, rsl);
  504.                                       {Read RecLen bytes into memory buffer}
  505.                                       {for the correct physical record}
  506.    RecNumber := RNum;                 {Set objectname.RecNumber = this record }
  507.    if CurRecord^[0] = GS_dBase_DltChr then DelFlag := true
  508.          else DelFlag := false;       {Set objectname.DelFlag to show status}
  509.                                       {of the record's Delete byte}
  510. END;                  {GetRec}
  511.  
  512.  
  513. {
  514.                                    INDEX
  515.  
  516.  
  517.      ╔══════════════════════════════════════════════════════════════════╗
  518.      ║                                                                  ║
  519.      ║   The INDEX method initializes the index array in objectname     ║
  520.      ║   and assigns the first index as the master index.  The other    ║
  521.      ║   index files will be updated upon .DBF updates (when the        ║
  522.      ║   index write entries are added).                                ║
  523.      ║                                                                  ║
  524.      ║       Calling the Method:                                        ║
  525.      ║                                                                  ║
  526.      ║           objectname.Index(String)                               ║
  527.      ║                                                                  ║
  528.      ║               ( where objectname is of type GS_dBase_DB,         ║
  529.      ║                       String is list of index files, separated   ║
  530.      ║                       by spaces.                                 ║
  531.      ║                                                                  ║
  532.      ║       Result:                                                    ║
  533.      ║                                                                  ║
  534.      ║           Index files are assigned and the master index is       ║
  535.      ║           opened.                                                ║
  536.      ║                                                                  ║
  537.      ╚══════════════════════════════════════════════════════════════════╝
  538. }
  539.  
  540.  
  541. Procedure GS_dBase_DB.Index (IName : String);
  542. var
  543.    i,j : integer;                     {Local working variable  }
  544.    st  : String[64];                  {Local working variable}
  545. begin
  546. {
  547.              ┌───────────────────────────────────────────────────┐
  548.              │  Reset index file array.                          │
  549.              │     1.  Close open index files                    │
  550.              │     2.  Release index objects stored on the heap  │
  551.              │     3.  Set array pointers to nil.                │
  552.              └───────────────────────────────────────────────────┘
  553. }
  554.    i := 1;
  555.    while dbfNdxTbl[i] <> nil do
  556.    begin
  557.       dbfNdxTbl[i]^.Ndx_Close;
  558.       Dispose(dbfNdxTbl[i]);
  559.       dbfNdxTbl[i] := nil;
  560.       inc(i);
  561.    end;
  562.    dbfNdxActv := false;               {Set index active flag to false}
  563. {
  564.            ┌──────────────────────────────────────────────────────┐
  565.            │  This routine scans the input string for the names   │
  566.            │  of index files.  Names must be separated by commas  │
  567.            │  or spaces.  The .NDX extension must not be part     │
  568.            │  of the file name                                    │
  569.            └──────────────────────────────────────────────────────┘
  570. }
  571.    i := 0;                            {i will hold count of index files}
  572.    j := 1;
  573.    st := '';
  574.    while j <= length(IName) do
  575.    begin
  576. {
  577.                ┌───────────────────────────────────────────────┐
  578.                │  Build an index file name in st until end of  │
  579.                │  input string, a comma, or a space is found   │
  580.                └───────────────────────────────────────────────┘
  581. }
  582.       if (IName[j] <> ' ') and (IName[j] <> ',') then
  583.          st := st + IName[j]
  584.       else
  585.       begin                           {When file string is complete:}
  586.          inc(i);                      {Increment index file count}
  587.          if st <> '' then             {   If not an empty string:  }
  588.          begin
  589.             New(dbfNdxTbl[i]);        {Get heap memory for index object}
  590.             if dbfNdxTbl[i]^.Init(st) then
  591.             begin                     {Initialize index object}
  592.             end;
  593.          end;
  594.          st := '';                    {Reset file name to empty for next}
  595.       end;
  596.       inc(j);                         {Inc counter for next input string char }
  597.    end;
  598. {
  599.               ┌─────────────────────────────────────────────────┐
  600.               │  This routine is needed to finish out when the  │
  601.               │  input string is finished.  Note the routine    │
  602.               │  above does not create an index entry at the    │
  603.               │  end of the input string.  That is done here.   │
  604.               └─────────────────────────────────────────────────┘
  605. }
  606.    if st <> '' then
  607.    begin
  608.       inc(i);
  609.       New(dbfNdxTbl[i]);
  610.       if dbfNdxTbl[i]^.Init(st) then
  611.       begin
  612.       end;
  613.    end;
  614.    if i > 0 then dbfNdxActv := true;  {Set index active flag true if index }
  615.                                       {files are found  }
  616. end;
  617.  
  618. {
  619.                                  INDEX_LIST
  620.  
  621.  
  622.      ╔══════════════════════════════════════════════════════════════════╗
  623.      ║                                                                  ║
  624.      ║   The INDEX_LIST method returns the index key field from the     ║
  625.      ║   index used as the master index.  This is done instead of the   ║
  626.      ║   normal action of reading the .DBF file.  Only the index file   ║
  627.      ║   is read during this method.  A common use of this method is    ║
  628.      ║   to build a memory table of keys and associated record numbers. ║
  629.      ║                                                                  ║
  630.      ║       Calling the Method:                                        ║
  631.      ║                                                                  ║
  632.      ║           objectname.Index_LIST(RecNum, String, RNum)            ║
  633.      ║                                                                  ║
  634.      ║               ( where objectname is of type GS_dBase_DB,         ║
  635.      ║                       RecAct is the index key to retrieve.       ║
  636.      ║                          (Top_Record, Next_Record,               ║
  637.      ║                           Prev_Record, or Bttm_Record)           ║
  638.      ║                                                                  ║
  639.      ║                       String is field to place key value.        ║
  640.      ║                       RNum is field to place record number.      ║
  641.      ║                                                                  ║
  642.      ║       Result:                                                    ║
  643.      ║                                                                  ║
  644.      ║           The master Index file is accessed based on RecAct.     ║
  645.      ║           The value in the key field entry is returned in        ║
  646.      ║           String.  The record's location id the .DBF file is     ║
  647.      ║           returned in RecNum.  File_EOF is set upon an attempt   ║
  648.      ║           to access beyond the last index entry.                 ║
  649.      ║                                                                  ║
  650.      ╚══════════════════════════════════════════════════════════════════╝
  651. }
  652.  
  653.  
  654. Procedure GS_dBase_DB.Index_List(RecAct: LongInt; var I_List;
  655.                                  var RNum : longint);
  656. var
  657.    I_L : string[255] absolute I_List;
  658.                                       {Redefines I_List for internal use}
  659. BEGIN
  660. {
  661.          ┌───────────────────────────────────────────────────────────┐
  662.          │  The next statement checks to see if an index is active   │
  663.          │  (dbfNdxActv = true), and calls the index object's        │
  664.          │  KeyRead method if true and the record requested is       │
  665.          │  a relative record (less than 0).  Note that the method   │
  666.          │  is called using the first index object pointer in array  │
  667.          │  dbfNdxTabl (the master index).                           │
  668.          └───────────────────────────────────────────────────────────┘
  669. }
  670.    if (dbfNdxActv) and (RecAct < 0) then
  671.    begin
  672.       RNum := dbfNdxTbl[1]^.KeyRead(RecAct);
  673.       if RNum > 0 then                {if good read, RNum will be > 0}
  674.       begin
  675.          I_L := dbfNdxTbl[1]^.Ndx_Key_St;
  676.                                       {get key value, and store in the}
  677.                                       {I_List variable, using I_L which}
  678.                                       {points to the same memory location}
  679.       end else
  680.       begin
  681.          RNum := 0;                   {set null value if no valid read}
  682.          I_L := '';                   {set null value if no valid read}
  683.       end;
  684.       File_EOF := dbfNdxTbl[1]^.KeyEOF;
  685.                                       {move index EOF flag to File_EOF};
  686.    end;
  687. end;
  688.  
  689. {
  690.                                 INIT
  691.  
  692.  
  693.      ╔══════════════════════════════════════════════════════════════════╗
  694.      ║                                                                  ║
  695.      ║   The INIT method initializes objectname by reading the .DBF     ║
  696.      ║   file and loading file structure information into the object.   ║
  697.      ║   It also checks for a memo file (.DBT) and assigns that to      ║
  698.      ║   a file type if it exists.  This routine must be called         ║
  699.      ║   before using the other methods in objectname.                  ║
  700.      ║                                                                  ║
  701.      ║       Calling the Method:                                        ║
  702.      ║                                                                  ║
  703.      ║           objectname.Init(String)                                ║
  704.      ║                                                                  ║
  705.      ║               ( where objectname is of type GS_dBase_DB,         ║
  706.      ║                       String is the file name of the dBase       ║
  707.      ║                       file (without the .DBF extension).         ║
  708.      ║                                                                  ║
  709.      ║       Result:                                                    ║
  710.      ║                                                                  ║
  711.      ║           DBase file object is initialized and memo file is      ║
  712.      ║           initialized.                                           ║
  713.      ║                                                                  ║
  714.      ╚══════════════════════════════════════════════════════════════════╝
  715. }
  716.  
  717. CONSTRUCTOR GS_dBase_DB.Init(FName : string);
  718. var
  719.    i : integer;                       {Local working variable}
  720.  
  721. {
  722.            ┌───────────────────────────────────────────────────────┐
  723.            │  The ProcessHeader Procedure stores information from  │
  724.            │  the dBase III .DBF file into objectname.             │
  725.            └───────────────────────────────────────────────────────┘
  726. }
  727.  
  728.    PROCEDURE ProcessHeader;
  729.    VAR
  730.       dFilea : FileRec absolute dFile;
  731.       StrFil : string[80];
  732.       WSt    : string[12];
  733.       Result : word;
  734.       ofs    : longint;
  735.       o, i   : Integer;               {Local working variables}
  736.       m,dy,y : string[2];             {Local working variables}
  737.    BEGIN             {ProcessHeader}
  738. {
  739.               ┌─────────────────────────────────────────────────┐
  740.               │  Test to ensure file is a dBase III .DBF file.  │
  741.               │  Exit with error if it is not.  Set the         │
  742.               │  objectname.WithMemo flag if memo file present. │
  743.               └─────────────────────────────────────────────────┘
  744. }
  745.       CASE HeadProlog.DBType OF
  746.          DB3File : WithMemo := False;
  747.          DB3WithMemo : WithMemo := True;
  748.          ELSE
  749.          BEGIN
  750.             GS_FileClose(dFile);      {If not a valid dBase file, close}
  751.             StrFil := '';
  752.             i := 0;
  753.             while dFilea.Name[i] <> #0 do
  754.             begin
  755.                StrFil := StrFil + dFilea.Name[i];
  756.                inc(i);
  757.             end;
  758.             StrFil := StrFil + ' not a dBase III file';
  759.             ShowError(157,StrFil);
  760.             Exit;
  761.          END;
  762.       END;                      {CASE}
  763. {
  764.                 ┌─────────────────────────────────────────────┐
  765.                 │  Convert numeric date fields to ASCII text  │
  766.                 └─────────────────────────────────────────────┘
  767. }
  768.       Str(HeadProlog.month,m);
  769.       if length(m) = 1 then m := '0'+m;
  770.       Str(HeadProlog.day,dy);
  771.       if length(dy) = 1 then dy := '0'+dy;
  772.       Str(HeadProlog.year,y);
  773.       if length(y) = 1 then y := '0'+y;
  774.       DateOfUpdate := m + '/' + dy + '/' + y;
  775.  
  776.       NumRecs := HeadProlog.RecCount; {Number of records in file}
  777.       HeadLen := HeadProlog.Location; {Starting byte location of first record}
  778.       RecLen := HeadProlog.RecordLen; {Length of each record}
  779.       RecNumber := 0;                 {Set current record to zero}
  780.       File_EOF := false;              {Set End of File flag to false}
  781.  
  782.       GetMem(Fields, HeadLen-33);     {Allocate memory for fields buffer.}
  783.                                       {Compute total header size as length of}
  784.                                       {header file information (32 bytes),}
  785.                                       {End of Header mark (1 byte), and the}
  786.                                       {field descriptors (32 bytes each).}
  787.                                       {Size - 33 = memory required by fields}
  788.  
  789.       NumFields := (HeadLen - 33) div 32;
  790.                                       {Each field descriptor is 32 bytes}
  791.                                       {Field descriptor area of header can}
  792.                                       {be divided by 32 to get field count}
  793.  
  794.       GS_FileRead(dFile, -1, Fields^, HeadLen-33, Result);
  795.                                       {Read field descriptor portion of header}
  796.  
  797.       GetMem(FieldsN, NumFields*12);  {Allocate memory for fields buffer.}
  798.  
  799.       ofs := 1;                       {Find offset for each field}
  800.       for i := 1 to NumFields do
  801.       begin
  802.          Fields^[i].FieldAddress := ofs;
  803.          ofs := ofs + Fields^[i].FieldLen;
  804.          move(Fields^[i].FieldName,WSt[1],11);
  805.          WSt[0] := #11;
  806.          WSt[0] := char(pred(pos(#0,WSt)));
  807.          WSt := TrimR(WSt);        {Remove trailing spaces}
  808.          FieldsN^[i] := WSt;
  809.       end;
  810.    END;                      {ProcessHeader}
  811.  
  812. {
  813.          ┌──────────────────────────────────────────────────────────┐
  814.          │  The GetHeader Procedure does the initial file read.     │
  815.          │  Reads the first 32 bytes of .DBF file.  This contains   │
  816.          │  information on record size, field descriptor size,      │
  817.          │  last date updated.  Starting point for all other        │
  818.          │  file structure information.                             │
  819.          └──────────────────────────────────────────────────────────┘
  820. }
  821.  
  822.    PROCEDURE GetHeader;
  823.    VAR
  824.       Result : Word;
  825.    BEGIN                { GetHeader }
  826.       GS_FileRead(dFile, 0, HeadProlog, 32, Result);
  827.       ProcessHeader;
  828.    END;                 { GetHeader }
  829.  
  830. {
  831.               ┌─────────────────────────────────────────────────┐
  832.               │  Beginning of INIT Procedure.  It does the      │
  833.               │  following:                                     │
  834.               │      1.  Assigns .DBF extension to the file.    │
  835.               │      2.  Opens the file.                        │
  836.               │      3.  Gets header information for the        │
  837.               │          objectname object.                     │
  838.               │      4.  Closes file.                           │
  839.               │      5.  Allocates memory for a record buffer   │
  840.               │      6.  Sets file status to 'Not Open'.        │
  841.               │      7.  Sets Index Active to false.            │
  842.               │      8.  If memo file, assigns a file type.     │
  843.               └─────────────────────────────────────────────────┘
  844. }
  845.  
  846. begin
  847.    Filename := FName+'.DBF';          {Assign .DBF file extension}
  848.    GS_FileAssign(dFile, FileName,8192);
  849.    GS_FileReset(dFile, 1);
  850.    GetHeader;                         {Load file structure information into}
  851.                                       {objectname}
  852.    GS_FileClose(dFile);               {Finished with file for now}
  853.    GetMem(CurRecord, RecLen);      {Allocate memory for record buffer}
  854.    dStatus := NotOpen;                {Set file status to 'Not Open'   }
  855.    dbfNdxActv := false;               {Set index active flag to false}
  856.    for i := 1 to 16 do dbfNdxTbl[i] := nil;
  857.                                       {Set index object pointer array to nil}
  858.    if WithMemo then
  859.    begin
  860.       GS_FileAssign(mFile, FName+'.DBT',2048);
  861.                                       {If a memo file is attached, then assign}
  862.                                       {it to a file type.  This must be done}
  863.                                       {here so all future objects can get to}
  864.                                       {the file if necessary.}
  865.    end;
  866.    GS_KeyI_Objt.Init;                 {Initialize parent object}
  867. end;
  868.  
  869. {
  870.                                      OPEN
  871.  
  872.  
  873.      ╔══════════════════════════════════════════════════════════════════╗
  874.      ║                                                                  ║
  875.      ║   The OPEN method checks to see if the file referenced by        ║
  876.      ║   objectname is already open.  If it is open, no other action    ║
  877.      ║   is taken.  If the file is not open, then it and its memo       ║
  878.      ║   file, if one exists, is opened and flags are set.              ║
  879.      ║                                                                  ║
  880.      ║       Calling the Method:                                        ║
  881.      ║                                                                  ║
  882.      ║           objectname.Open                                        ║
  883.      ║                                                                  ║
  884.      ║               ( where objectname is of type GS_dBase_DB )        ║
  885.      ║                                                                  ║
  886.      ║       Result:                                                    ║
  887.      ║                                                                  ║
  888.      ║           1.  If file already opened, no action is taken.        ║
  889.      ║                                                                  ║
  890.      ║           otherwise:                                             ║
  891.      ║                                                                  ║
  892.      ║           1.  .DBF file is opened.                               ║
  893.      ║           2.  File status set to 'Not Updated'.                  ║
  894.      ║           3.  If memo file exists, .DBT file is opened.          ║
  895.      ║           4.  Current record number is set to zero.              ║
  896.      ║                                                                  ║
  897.      ╚══════════════════════════════════════════════════════════════════╝
  898. }
  899.  
  900.  
  901. PROCEDURE GS_dBase_DB.Open;
  902. BEGIN              { GS_dBase_Open }
  903.    if dStatus = NotOpen then          {Do only if file not already open}
  904.    begin
  905.       GS_FileAssign(dFile, FileName,4096);
  906.       GS_FileReset(dFile, 1);         {Open .DBF file
  907.       dStatus := NotUpdated;          {Set status to 'Not Updated' }
  908.       if WithMemo then GS_FileReset(mFile,GS_dBase_MaxMemoRec);
  909.                                       {If memo file, then open .DBT file}
  910.       RecNumber := 0;                 {Set current record to zero }
  911.       Blank;                          {Clear the record buffer}
  912.    end;
  913. END;               { GS_dBase_Open }
  914.  
  915. {
  916.                                  PUTREC
  917.  
  918.  
  919.      ╔══════════════════════════════════════════════════════════════════╗
  920.      ║                                                                  ║
  921.      ║   The PUTREC method will write an updated record to the dBase    ║
  922.      ║   III(+) .DBF file.  The data to be written must be stored       ║
  923.      ║   in objectname.CurRecord^ prior to calling the method.          ║
  924.      ║                                                                  ║
  925.      ║       Calling the Method:                                        ║
  926.      ║                                                                  ║
  927.      ║           objectname.PutRec(RecNum)                              ║
  928.      ║                                                                  ║
  929.      ║               ( where objectname is of type GS_dBase_DB,         ║
  930.      ║                       RecNum is physical record number to        ║
  931.      ║                       write to.  If not within the range of      ║
  932.      ║                       existing records, it record will be        ║
  933.      ║                       appended to the end of the file.           ║
  934.      ║                                                                  ║
  935.      ║       Result:                                                    ║
  936.      ║                                                                  ║
  937.      ║           1.  If RecNum not in range of existing records         ║
  938.      ║               it will be appended and objectname.NumRecs         ║
  939.      ║               incremented by one.                                ║
  940.      ║           2.  Record will be written.                            ║
  941.      ║           3.  RecNum will become current record number.          ║
  942.      ║           4.  File status will be changed to 'Updated'.          ║
  943.      ║                                                                  ║
  944.      ╚══════════════════════════════════════════════════════════════════╝
  945. }
  946.  
  947.  
  948. PROCEDURE GS_dBase_DB.PutRec(RecNum : LongInt);
  949. VAR
  950.    Result : Word;                     {Local Variable}
  951.    RNum   : LongInt;                  {Local Variable}
  952.    IKey   : String;                   {Local Variable for Key Formula string}
  953. BEGIN
  954.    RNum := RecNum;                    {Move RecNum to local variable for }
  955.                                       {possible modification}
  956. {
  957.                 ┌─────────────────────────────────────────────┐
  958.                 │  If Record Number not in range of existing  │
  959.                 │  records, append it to the end of file.     │
  960.                 └─────────────────────────────────────────────┘
  961. }
  962.    IF (RNum > NumRecs) or (RNum < 1) then
  963.    begin
  964.       inc(NumRecs);                   {Increment record count}
  965.       RNum := NumRecs;                {Put last record number in RNum}
  966.    end;
  967.    GS_FileWrite(dFile, HeadLen+(RNum-1)*RecLen, CurRecord^, RecLen, Result);
  968.    RecNumber := RNum;              {Store record number as current record }
  969.    dStatus := Updated;             {Set file status to 'Updated'}
  970. {
  971.          ┌───────────────────────────────────────────────────────────┐
  972.          │  The next statement checks to see if an index is active   │
  973.          │  (dbfNdxActv = true), and calls the index object's        │
  974.          │  KeyUpdate method if true.   Note that the method         │
  975.          │  is called using the first index object pointer in array  │
  976.          │  dbfNdxTabl (the master index).                           │
  977.          └───────────────────────────────────────────────────────────┘
  978. }
  979.    if (dbfNdxActv) then
  980.    begin
  981.       dbfNdxTbl[1]^.KeyUpdate(Formula(dbfNdxTbl[1]^.Ndx_Key_Form),RNum,RecNum);
  982.    end;
  983. END;                        {PutRec}
  984. {.pa}
  985. {
  986.                                   UNDELETE
  987.  
  988.  
  989.    ╔═══════════════════════════════════════════════════════════════════════╗
  990.    ║                                                                       ║
  991.    ║  The UNDELETE method will reset the Delete flag in the dBase III(+)   ║
  992.    ║  file.                                                                ║
  993.    ║                                                                       ║
  994.    ║      Calling the Method:                                              ║
  995.    ║                                                                       ║
  996.    ║            objectname.UnDelete                                        ║
  997.    ║                                                                       ║
  998.    ║                   ( where objectname is of type GS_dBase_DB)          ║
  999.    ║                                                                       ║
  1000.    ║       Result:                                                         ║
  1001.    ║                                                                       ║
  1002.    ║            1.  objectname.DelFlag is set false.                       ║
  1003.    ║            2.  A ' ' (UnDelete flag) is set in byte 0 of current      ║
  1004.    ║                file.                                                  ║
  1005.    ║            3.  PutRec is called to write current record to disk.      ║
  1006.    ║                                                                       ║
  1007.    ╚═══════════════════════════════════════════════════════════════════════╝
  1008. }
  1009.  
  1010.  
  1011. PROCEDURE GS_dBase_DB.UnDelete;
  1012. begin
  1013.    DelFlag := false;                  {Set Delete flag to false}
  1014.    CurRecord^[0] := GS_dBase_UnDltChr;
  1015.                                       {Put ' ' in first byte of current record}
  1016.    PutRec(RecNumber);                 {Write the current record to disk }
  1017. end;
  1018.  
  1019. begin
  1020. end.
  1021.  
  1022.  
  1023.